package org.hyena.cvms.aaatest;

import org.apache.commons.beanutils.ConversionException;
import org.springframework.expression.ExpressionException;

/**
 * 运算工具类，解析字符串运算式,根据实际使用频率，可以考虑不将方法设为静态
 * @author lgf
 * @date 2021年9月30日 上午9:44:22
 */
public final class CalculationUtils {

	/**
	 * 表达式必须是boolean类型的，否则报错
	 * @author: lgf
	 * @date 2021年10月8日 上午8:55:10
	 * @param calculationString
	 * @return
	 * @version
	 */
	public static Boolean getBoolean(String calculationString) {
		calculationString = format(calculationString);
		try {
			Object res = getObject(calculationString);
			if(res != null && res instanceof Boolean)return (Boolean)res;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	public static Double getDouble(String calculationString) {
		calculationString = format(calculationString);
		try {
			Object res = getObject(calculationString);
			if(res != null && res instanceof Double)return (Double)res;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static Double getDouble(String calculationString,int scale) {
		calculationString = format(calculationString);
		try {
			Object res = getObject(calculationString);
			if(res != null && res instanceof Double)return round((Double)res,scale);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	/**
	 * 根据表达式类型，返回Double或Boolean
	 * @author: lgf
	 * @date 2021年10月8日 上午8:54:04
	 * @param calculationString
	 * @return
	 * @version
	 */
	public static Object getResult(String calculationString) {
		calculationString = format(calculationString);
		try {
			return getObject(calculationString);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	public static Integer getInt(String calculationString) {
		Object res;
		try {
			calculationString = format(calculationString);
			res = getObject(calculationString);
			if(res != null && res instanceof Double)return ((Double)res).intValue();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The   scale   must   be   a   positive   integer   or   zero");
        }
        java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v));
        java.math.BigDecimal one = new java.math.BigDecimal("1");
        return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }
	
    /**
     * 格式预处理,去空格，回车等
     * @author: lgf
     * @date 2021年9月30日 上午9:46:19
     * @param calculationString
     * @return
     * @version
     */
	private static String format(String calculationString) {
    	return calculationString.replaceAll("\\s*|\\t|\\r|\\n", "");
    }

    /**
     * 可能返回的类型，boolean，double，string
     * <p>递归去括号
     * @author: lgf
     * @date 2021年10月8日 下午1:11:59
     * @param calculationString
     * @return
     * @throws Exception
     * @version
     */
    private static Object getObject(String calculationString) throws Exception{
    	 int a = calculationString.indexOf("("); 
    	 if(a > -1) {
    		  String p1 = calculationString.substring(0,a);
    		  Object p2 = getObject(calculationString.substring(a+1)); // 递归字符串，该字符串去除第一个(,下一层会去掉对应的)
    		  if(p1.length()==0 && !(p2 instanceof String))return p2;
    		  String newstr = p1+p2;
    		  if( (a = newstr.indexOf(")")) > -1 ) { // 递归结果还有),表示还有括号可以递归
    			  return getObject(newstr);
    		  }else { // 递归后没有括号了，直接返回计算结果
    			  return getSingleObject(newstr);
    		  }
         }else { // 获得并计算优先级最高的括号内容（里面没有括号了）
           	int i1 = calculationString.indexOf(")");
           	if(i1 > -1) { // 有) 时需要分割后运算,返回时去除第一个)
           		String h1 = calculationString.substring(0,i1);
           		String h2 =  calculationString.substring(i1+1);
           		if(h2.length()>0)return getSingleObject(h1)+h2; // )后面还有算式字符串p2，运算结果拼接p2，并返回
           		return getSingleObject(h1); // 直接返回运算结果，类型为boolean或double。
           	}
           	return getSingleObject(calculationString);  // 直接返回运算结果，类型为boolean或double
         }
    }
  
    /**
     * 计算无括号的运算式,可能返回boolean、double
     * <p>计算的表达式如：1+3-6*2/2 == 3 或 1+3-6*2/2
     * @author: lgf
     * @date 2021年9月30日 下午4:32:42
     * @param calculationString
     * @return
     * @version
     */
    private static Object getSingleObject(String calculationString) throws ConversionException{
	    int cou = 0;
	    String regx = null;
	    if(calculationString.indexOf(">=")>-1) {
	    	cou++;regx = ">=";
	    }else if(calculationString.indexOf(">")>-1) {
		    cou++;regx = ">";
	    }
	    if(calculationString.indexOf("<=")>-1) {
	    	cou++;regx = "<=";
	    }else if(calculationString.indexOf("<")>-1) {
		    cou++;regx = "<";
	    }
	    if(calculationString.indexOf("==")>-1) {
	    	cou++;regx = "==";
	    }
	    switch (cou) {
		case 0: // 返回数字
		    return computeNumber(calculationString);
		case 1: // 返回boolean
			String[] two = calculationString.split(regx);
			if(two == null || two.length != 2) {
				System.out.println("错误的格式1："+calculationString);
				return null;
			}
			return compareBoolean(computeNumber(two[0]),regx,computeNumber(two[1]));
		default: // 错误的格式
		    System.out.println("错误的格式："+calculationString);
			return null;
		}
	}
    
    /**
     * 计算无括号算式，如 1+2*5-8 。只返回null或double。算式异常时返回null
     * @author: lgf
     * @date 2021年10月8日 上午11:45:56
     * @param a
     * @return
     * @version
     */
    private static Double computeNumber(String a) {
    	if(a == null)return null;
    	char[] chars = a.toCharArray();
    	Double tem = null,m1=null;
    	int f=0;
    	char ty = 0,f1=0,f2=0; // 1+ 2- 3 * 4/
    	for(int i=0;i<chars.length;i++) {
    		char t = chars[i];
    		if(t<42 || t== 44 || t > 57)throw new ExpressionException("出现了不应该出现的字符："+t);; // 只允许的符号   0至9 + - . / * 
    		if(t < 48 && t != 46 && i > 0 && chars[i-1] > 47 && chars[i-1] < 58) { // + - * /
    			if(t=='-' || t=='+') {
    				if(m1 != null) {
    					Double ddd= Double.valueOf(String.valueOf(chars, f, i-f));
        				if(f1 > 0) {
        					tem =  computeNormal(f1,tem,computeNormal(f2,m1,ddd));
        				}else {
        					tem = computeNormal(f2,m1,ddd);
        				}
        				m1 = null;
        			}else if(f1=='-') {
        				tem =tem - Double.valueOf(String.valueOf(chars, f, i-f));
        			}else if (f1 == '+') {
        				tem = tem + Double.valueOf(String.valueOf(chars, f, i-f));
        			}else { // 首次
        				tem = Double.valueOf(String.valueOf(chars, f, i-f));
        			}
        			f1 = t;
    			}else {
    				if(m1 != null) {
    					m1 = computeNormal(f2,m1,Double.valueOf(String.valueOf(chars, f, i-f)));
        			}else {
        				m1 = Double.valueOf(String.valueOf(chars, f, i-f));
        			}
    				f2 = t;
    			}
    			ty = t;
    			f = i + 1;
    		}
    		if(i == chars.length-1) {
    			Double ddd= Double.valueOf(String.valueOf(chars, f,  i-f+1));
    			if(f == 0)return ddd;
    			if(m1 != null) {
    				if(f1 > 0) {
    					return computeNormal(f1,tem,computeNormal(f2,m1,ddd));
    				}
    				return computeNormal(f2,m1,ddd);
    			}
    			return computeNormal(ty,tem,ddd);
    		}
    	}
    	throw new ExpressionException("错误的表达式");
    }
    
    /**
     * 比较两个double数据大小,需要传入字符型的 “==” , ">=" , ">" 等
     * @author: lgf
     * @date 2021年9月30日 下午5:36:38
     * @param v1
     * @param tag
     * @param v2
     * @return
     * @version
     */
    private static Boolean compareBoolean(Double v1,String tag,Double v2) {
    	if("==".equals(tag)) {
			return v1.equals(v2);
		}else if(">=".equals(tag)) {
			return v1 >= v2;
		}else if("<=".equals(tag)) {
			return v1 <= v2;
		}else if(">".equals(tag)) {
			return v1 > v2;
		}else if("<".equals(tag)) {
			return v1 < v2;
		}else {
			return false;
		}
    }
    
    private static Double computeNormal(char sign,Double d1,Double d2) {
    	if(d1==null || d2== null)return null;
    	switch(sign) {
    	    case '-':
    	    	return d1 - d2;
    	    case '+':
    	    	return d1 + d2;
    	    case '*':
    	    	return d1 * d2;
			case '/':
				return d1 / d2;
			default :
				return null;
    	}
    }
}
